package site.jlopen.assets.shiro;

import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.shiro.authc.Authenticator;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import jakarta.servlet.Filter;
import site.jlopen.assets.shiro.filter.ApiAuthenticationFilter;
import site.jlopen.assets.shiro.filter.WebAuthenticationFilter;
import site.jlopen.assets.shiro.match.CredentialsMatch;
import site.jlopen.assets.shiro.match.MyCredentialsMatcher;
import site.jlopen.assets.shiro.realm.JwtRealm;
import site.jlopen.assets.shiro.realm.MyAtLeastOneSuccessfulStrategy;
import site.jlopen.assets.shiro.realm.WebRealm;

/**
* @ClassName: ShiroConfig
* @Description: <p>TODO</p>
* @author ChenJiaLu
* @date 2022年1月18日 下午5:52:15
*
 */
@Configuration
public class ShiroConfig {
	
	// 设置session过期时间2小时，1000L是毫秒，前面是秒
	private final static long SESSIONTIMEOUT = 60*60*2*1000L;

	//@Value("${wites.include}")
	//private String[] shiroWites;
	
	private List<String> wites(){
		List<String> list = new ArrayList<String>();
		list.add("/login");
		list.add("/logon");
		list.add("/register");
		list.add("/error");
		list.add("/plug/**");
		list.add("/base/**");
		list.add("/favicon.ico");
		list.add("/doc.html");
		list.add("/swagger-ui.html");
	    return list;
	}
	
    // 1、创建shiroFilter  负责拦截所有的请求
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        
        Map<String, Filter> filters = factoryBean.getFilters();
        // 将重写的Filter注入到factoryBean的filter中
     	filters.put("webAuthc", new WebAuthenticationFilter());
     	filters.put("apiAuthc", new ApiAuthenticationFilter());

        // 给filter设置安全管理器
        factoryBean.setSecurityManager(defaultWebSecurityManager);
        // 配置系统受限资源
        // 配置系统公共资源
        Map<String,String> map = new HashMap<String, String>();

        // anon设置为公共资源，不需要认证和授权
        //if(null != shiroWites) {
        	for (String shiroWite : wites()) {
        		 map.put(shiroWite,"anon");
			}
        //}
        //map.put("/login","anon");
        //map.put("/logon","anon");
        //map.put("/register","anon");
        //map.put("/error","anon");
        //map.put("/plug/**","anon");
        //map.put("/css/**","anon");
        //map.put("/js/**","anon");
        //map.put("/img/group.svg","anon");
        //map.put("/img/**","anon");
        //map.put("/favicon.ico","anon");
        //map.put("/doc.html","anon");
        //map.put("/swagger-ui.html","anon");
        
        // authc代表请求这个资源需要认证和授权
     	
        map.put("/**", "webAuthc");
        map.put("/api/**", "apiAuthc");
        //map.put("/**", "authc");

        // 默认认证界面路径
     	factoryBean.setLoginUrl("/login");
     	factoryBean.setSuccessUrl("/index");
     	factoryBean.setUnauthorizedUrl("/error");
     	factoryBean.setFilters(filters);
        factoryBean.setFilterChainDefinitionMap(map);
        return factoryBean;
    }
	
    @Bean
    public ApiAuthenticationFilter getJwtFilter(){
        return new ApiAuthenticationFilter();
    }
    
    
    /**
     * 2、创建安全管理器
     * @return
     */
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(WebRealm webRealm, JwtRealm jwtRealm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        //defaultWebSecurityManager.setRealm(realm);
        List<Realm> realms = new ArrayList<Realm>();
        realms.add(webRealm);
        realms.add(jwtRealm);
        defaultWebSecurityManager.setRealms(realms);
        defaultWebSecurityManager.setCacheManager(cacheManager());
        defaultWebSecurityManager.setRememberMeManager(rememberMeManager());
        defaultWebSecurityManager.setSessionManager(sessionManager());
        
        Authenticator authenticator = defaultWebSecurityManager.getAuthenticator();
        if(authenticator instanceof ModularRealmAuthenticator){
          ModularRealmAuthenticator modularRealmAuthenticator = (ModularRealmAuthenticator) authenticator;
          modularRealmAuthenticator.setAuthenticationStrategy(new MyAtLeastOneSuccessfulStrategy());
        }
        
        ThreadContext.bind(defaultWebSecurityManager);
        return defaultWebSecurityManager;
    }

    /**
     * 3、创建自定义realm
     */
	@Autowired private CredentialsMatch credentialsMatch;
	@Autowired private MyCredentialsMatcher jwtCredentialsMatch;
	
	/*@Bean
	@Primary
	public Realm getRealm(){
	    WebRealm customerRealm = new WebRealm();
	    customerRealm.setCredentialsMatcher(credentialsMatch);
	    return customerRealm;
	}*/
	
	@Bean
	public WebRealm getWebRealm() {
		WebRealm realm = new WebRealm();
		realm.setCredentialsMatcher(credentialsMatch);
		return realm;
	}
	@Bean
	public JwtRealm getJwtRealm() {
		JwtRealm realm = new JwtRealm();
		realm.setCredentialsMatcher(jwtCredentialsMatch);
		return realm;
	}
    
    @Bean
    public CookieRememberMeManager rememberMeManager() {
    	CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        cookieRememberMeManager.setCipherKey(Base64.getMimeDecoder().decode("JhtzJYERYOVtsudMq1KUOQ=="));
        return cookieRememberMeManager;
    }
    
    public SimpleCookie rememberMeCookie() {
    	 //这个参数是cookie的名称，对应前端的checkbox的name=rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("kuscia-web-RmbMe");
        //cookie生效时间七天
        simpleCookie.setMaxAge(7*24*60*60);
        return simpleCookie;
    }
    
    @Bean(name = "sessionManager")
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionIdCookie(getSimpleCookie());
        sessionManager.setGlobalSessionTimeout(SESSIONTIMEOUT);
        return sessionManager;
    }
    
	public SimpleCookie getSimpleCookie() {
	    SimpleCookie simpleCookie = new SimpleCookie("kuscia-web");
	    simpleCookie.setHttpOnly(true);
	    simpleCookie.setMaxAge(-1);
        //cookie生效时间为10秒
        simpleCookie.setMaxAge(100000);
	    return simpleCookie;
	}
	
	/*@Bean
	public RedisCacheManager redisCacheManager(){
	    RedisCacheManager redisCacheManager = new RedisCacheManager();
	    redisCacheManager.setRedisManager(redisManager());
	    return redisCacheManager;
	}*/
	
	@Bean
	public CacheManager cacheManager() {
	    return new MemoryConstrainedCacheManager();
	}
	
	/*@Bean
	public RedisManager redisManager(){
	    return new RedisManager();
	}*/
    
	/*@Bean
	public CredentialsMatch customCredentialsMatch() {
	//根据自身项目情况配置，不可完全复制
		CredentialsMatch customCredentialsMatch = new CredentialsMatch();
	    customCredentialsMatch.setHashAlgorithmName("md5");
	    //此处配置修改需注意,会导致密码验证失败
	    customCredentialsMatch.setHashIterations(1024);
	    customCredentialsMatch.setStoredCredentialsHexEncoded(true);
	    return customCredentialsMatch;
	}*/
    
    /**
     * 配置方言支持（就是将shrio的语法转为tymeleaf的语法）
     */
    @Bean
    public ShiroDialect getshirodialect() {
        return new ShiroDialect();
    }
    
    /**
     * 开启shiro aop注解支持，不开启的话权限验证就会失效
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }
}
